package org.skycloud.mybatis.filter.wrapper;

import java.util.List;
import java.util.Set;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.reflection.wrapper.BaseWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.skycloud.mybatis.filter.Filter;

public class FilterObjectWrapperFactory implements ObjectWrapperFactory {

	@Override
	public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
		if (object instanceof Filter) {
			return new FilterObjectWrapper(metaObject, (Filter) object);
		}
		throw new RuntimeException("FilterObjectWrapperFactory should never provide other ObjectWrappers.");
	}

	@Override
	public boolean hasWrapperFor(Object object) {
		return object instanceof Filter;
	}

	private static class FilterObjectWrapper extends BaseWrapper {

		private final Filter filter;

		FilterObjectWrapper(MetaObject metaObject, Filter filter) {
			super(metaObject);
			this.filter = filter;
		}

		@Override
		public Object get(PropertyTokenizer prop) {
			if (prop.getIndex() != null) {
				Object collection = resolveCollection(prop, filter);
				return getCollectionValue(prop, collection);
			} else {
				return filter.get(prop.getName());
			}
		}

		@Override
		public String findProperty(String name, boolean useCamelCaseMapping) {
			return name;
		}

		@Override
		public boolean hasGetter(String name) {
			return filter.getPropertyNames().contains(name);
		}

		@Override
		public String[] getGetterNames() {
			Set<String> propertyNames = filter.getPropertyNames();
			return propertyNames.toArray(new String[propertyNames.size()]);
		}

		@Override
		public Class<?> getGetterType(String name) {
			PropertyTokenizer prop = new PropertyTokenizer(name);
			if (prop.hasNext()) {
				MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
				if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
					return Object.class;
				} else {
					return metaValue.getGetterType(prop.getChildren());
				}
			} else {
				Object object = filter.get(name);
				if (object != null) {
					return object.getClass();
				} else {
					return Object.class;
				}
			}
		}

		@Override
		public boolean isCollection() {
			return false;
		}

		@Override
		public void set(PropertyTokenizer prop, Object value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public String[] getSetterNames() {
			throw new UnsupportedOperationException();
		}

		@Override
		public Class<?> getSetterType(String name) {
			throw new UnsupportedOperationException();
		}

		@Override
		public boolean hasSetter(String name) {
			throw new UnsupportedOperationException();
		}

		@Override
		public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void add(Object element) {
			throw new UnsupportedOperationException();
		}

		@Override
		public <E> void addAll(List<E> element) {
			throw new UnsupportedOperationException();
		}
	}
}
